///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#include "ExtrasPrerequisites.h "

#include "OgreMovableObject.h"
#include "OgreRenderable.h"
#include "OgreResourceGroupManager.h"


namespace Extras
{
	
	class _PhiloExtrasExport ManualObject : public MovableObject
	{
	public:
		ManualObject(const Ogre::String& name);
		virtual ~ManualObject();

		//pre-declare ManualObjectSection
		class ManualObjectSection;

		virtual void clear(void);
		
		virtual void estimateVertexCount(size_t vcount);

		virtual void estimateIndexCount(size_t icount);

		virtual void begin(const Ogre::String& materialName,
			RenderOperation::OperationType opType = RenderOperation::OT_TRIANGLE_LIST, const Ogre::String & groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

		virtual void setDynamic(bool dyn) { mDynamic = dyn; }
		
		virtual bool getDynamic() const { return mDynamic; }

		virtual void beginUpdate(size_t sectionIndex);
		
		virtual void position(const Vector3& pos);
		/// @copydoc ManualObject::position(const Vector3&)
		virtual void position(Real x, Real y, Real z);

		virtual void normal(const Vector3& norm);
		/// @copydoc ManualObject::normal(const Vector3&)
		virtual void normal(Real x, Real y, Real z);

		virtual void tangent(const Vector3& tan);
		/// @copydoc ManualObject::tangent(const Vector3&)
		virtual void tangent(Real x, Real y, Real z);

		virtual void textureCoord(Real u);
		/// @copydoc ManualObject::textureCoord(Real)
		virtual void textureCoord(Real u, Real v);
		/// @copydoc ManualObject::textureCoord(Real)
		virtual void textureCoord(Real u, Real v, Real w);
		/// @copydoc ManualObject::textureCoord(Real)
		virtual void textureCoord(Real x, Real y, Real z, Real w);
		/// @copydoc ManualObject::textureCoord(Real)
		virtual void textureCoord(const Vector2& uv);
		/// @copydoc ManualObject::textureCoord(Real)
		virtual void textureCoord(const Vector3& uvw);
		/// @copydoc ManualObject::textureCoord(Real)
		virtual void textureCoord(const Vector4& xyzw);

		virtual void colour(const ColourValue& col);
		
		virtual void colour(Real r, Real g, Real b, Real a = 1.0f);

		virtual void index(uint32 idx);
		
		virtual void triangle(uint32 i1, uint32 i2, uint32 i3);
		
		virtual void quad(uint32 i1, uint32 i2, uint32 i3, uint32 i4);
		/// Get the number of vertices in the section currently being defined (returns 0 if no section is in progress).
		virtual size_t getCurrentVertexCount() const;
		/// Get the number of indices in the section currently being defined (returns 0 if no section is in progress).
		virtual size_t getCurrentIndexCount() const;
		
		virtual ManualObjectSection* end(void);

		virtual void setMaterialName(size_t subindex, const Ogre::String& name, const Ogre::String & group = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

		virtual MeshPtr convertToMesh(const Ogre::String& meshName, 
			const Ogre::String& groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

		void setUseIdentityProjection(bool useIdentityProjection);

		bool getUseIdentityProjection(void) const { return mUseIdentityProjection; }

		void setUseIdentityView(bool useIdentityView);

		bool getUseIdentityView(void) const { return mUseIdentityView; }

		void setBoundingBox(const AxisAlignedBox& box) { mAABB = box; }

		ManualObjectSection* getSection(unsigned int index) const;

		unsigned int getNumSections(void) const;
		
		void setKeepDeclarationOrder(bool keepOrder) { mKeepDeclarationOrder = keepOrder; }

		bool getKeepDeclarationOrder() const { return mKeepDeclarationOrder; }
		// MovableObject overrides
		const Ogre::String& getMovableType(void) const;
		
		const AxisAlignedBox& getBoundingBox(void) const;
		
		Real getBoundingRadius(void) const;
		
		void _updateRenderQueue(RenderQueue* queue);
		
		EdgeData* getEdgeList(void);
		
		bool hasEdgeList(void);
		
		ShadowRenderableListIterator getShadowVolumeRenderableIterator(
			ShadowTechnique shadowTechnique, const Light* light, 
			HardwareIndexBufferSharedPtr* indexBuffer, 
			bool extrudeVertices, Real extrusionDist, unsigned long flags = 0);

		/// Built, renderable section of geometry
		class _PhiloExtrasExport ManualObjectSection : public Renderable, public MemAlloc_SceneObj
		{
		protected:
			ManualObject* mParent;
			Ogre::String mMaterialName;
			Ogre::String mGroupName;
			mutable MaterialPtr mMaterial;
			RenderOperation mRenderOperation;
			bool m32BitIndices;

		public:
			ManualObjectSection(ManualObject* parent, const Ogre::String& materialName,
				RenderOperation::OperationType opType, const Ogre::String & groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
			virtual ~ManualObjectSection();
			
			/// Retrieve render operation for manipulation
			RenderOperation* getRenderOperation(void);
			/// Retrieve the material name in use
			const Ogre::String& getMaterialName(void) const { return mMaterialName; }
			/// Retrieve the material group in use
			const Ogre::String& getMaterialGroup(void) const { return mGroupName; }
			/// update the material name in use
			void setMaterialName(const Ogre::String& name, const Ogre::String& groupName = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME );
			/// Set whether we need 32-bit indices
			void set32BitIndices(bool n32) { m32BitIndices = n32; }
			/// Get whether we need 32-bit indices
			bool get32BitIndices() const { return m32BitIndices; }
			
			// Renderable overrides
			const MaterialPtr& getMaterial(void) const;
			
			void getRenderOperation(RenderOperation& op);
			
			void getWorldTransforms(Matrix4* xform) const;
			
			Real getSquaredViewDepth(const Ogre::Camera *) const;
			
			const LightList &getLights(void) const;
			
		};
		
		class _PhiloExtrasExport ManualObjectSectionShadowRenderable : public ShadowRenderable
		{
		protected:
			ManualObject* mParent;
			// Shared link to position buffer
			HardwareVertexBufferSharedPtr mPositionBuffer;
			// Shared link to w-coord buffer (optional)
			HardwareVertexBufferSharedPtr mWBuffer;

		public:
			ManualObjectSectionShadowRenderable(ManualObject* parent, 
				HardwareIndexBufferSharedPtr* indexBuffer, const VertexData* vertexData, 
				bool createSeparateLightCap, bool isLightCap = false);
			~ManualObjectSectionShadowRenderable();
			/// Overridden from ShadowRenderable
			void getWorldTransforms(Matrix4* xform) const;
			HardwareVertexBufferSharedPtr getPositionBuffer(void) { return mPositionBuffer; }
			HardwareVertexBufferSharedPtr getWBuffer(void) { return mWBuffer; }
			/// Overridden from ShadowRenderable
			virtual void rebindIndexBuffer(const HardwareIndexBufferSharedPtr& indexBuffer);

		};

		typedef Ogre::vector<ManualObjectSection*>::type SectionList;

		/// @copydoc MovableObject::visitRenderables
		void visitRenderables(Renderable::Visitor* visitor, 
			bool debugRenderables = false);
		
		
	protected:
		/// Dynamic?
		bool mDynamic;
		/// List of subsections
		SectionList mSectionList;
		/// Current section
		ManualObjectSection* mCurrentSection;
		/// Are we updating?
		bool mCurrentUpdating;
		/// Temporary vertex structure
		struct TempVertex
		{
			Vector3 position;
			Vector3 normal;
			Vector3 tangent;
			Vector4 texCoord[OGRE_MAX_TEXTURE_COORD_SETS];
			ushort texCoordDims[OGRE_MAX_TEXTURE_COORD_SETS];
			ColourValue colour;
		};
		/// Temp storage
		TempVertex mTempVertex;
		/// First vertex indicator
		bool mFirstVertex;
		/// Temp vertex data to copy?
		bool mTempVertexPending;
		/// System-memory buffer whilst we establish the size required
		char* mTempVertexBuffer;
		/// System memory allocation size, in bytes
		size_t mTempVertexSize;
		/// System-memory buffer whilst we establish the size required
		uint32* mTempIndexBuffer;
		/// System memory allocation size, in bytes
		size_t mTempIndexSize;
		/// Current declaration vertex size
		size_t mDeclSize;
		/// Estimated vertex count
		size_t mEstVertexCount;
		/// Estimated index count
		size_t mEstIndexCount;
		/// Current texture coordinate
		ushort mTexCoordIndex;
		/// Bounding box
		AxisAlignedBox mAABB;
		/// Bounding sphere
		Real mRadius;
		/// Any indexed geometry on any sections?
		bool mAnyIndexed;
		/// Edge list, used if stencil shadow casting is enabled 
		EdgeData* mEdgeList;
		/// List of shadow renderables
		ShadowRenderableList mShadowRenderables;
		/// Whether to use identity projection for sections
		bool mUseIdentityProjection;
		/// Whether to use identity view for sections
		bool mUseIdentityView;
		/// Keep declaration order or let the queue optimize it
		bool mKeepDeclarationOrder;

		/// Delete temp buffers and reset init counts
		virtual void resetTempAreas(void);
		/// Resize the temp vertex buffer?
		virtual void resizeTempVertexBufferIfNeeded(size_t numVerts);
		/// Resize the temp index buffer?
		virtual void resizeTempIndexBufferIfNeeded(size_t numInds);
		/// Copy current temp vertex into buffer
		virtual void copyTempVertexToBuffer(void);

	};

	
	class _PhiloExtrasExport ManualObjectFactory : public MovableObjectFactory
	{
	protected:
		MovableObject* createInstanceImpl( const Ogre::String& name, const NameValuePairList* params);
	public:
		ManualObjectFactory() {}
		~ManualObjectFactory() {}

		static Ogre::String FACTORY_TYPE_NAME;

		const Ogre::String& getType(void) const;
		void destroyInstance( MovableObject* obj);  

	};
	
	
}


